﻿// People should be able to see and report errors to the developer very easily.
//
// Unity's Developer Console only works in development builds and it only shows
// errors. This class provides a console that works in all builds and also shows
// log and warnings in development builds.
//
// Note: we don't include the stack trace, because that can also be grabbed from
// the log files if needed.
//
// Note: there is no 'hide' button because we DO want people to see those errors
// and report them back to us.
//
// Note: normal Debug.Log messages can be shown by building in Debug/Development
//       mode.
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System;
using System.Text;
using System.Collections;

namespace Mirror
{

    //1:保存启动后的1000条log,如果这1000条中有重要log，则立即保存初始化log，并从重要log开始计数100条log 进行保存，如果保存后，没有连续的100条正常log，则后续log都不保存，如果有连续100条log，状态又恢复到之前，只要有重要log，就计数100条然后保存，循环往复

    public class GUIConsoleV2 : MonoBehaviour
    {
        enum logFileType { 
            _IInitLog,
            _ImportLog
        }

        struct LogEntry
        {
            public string message;
            public LogType type;

            public LogEntry(string message, LogType type) {
                this.message = message;
                this.type = type;
            }
        }

        public bool LowLogMode = true;

        public string ImportLogStorePath = "";

        public int height = 150;

        // only keep the recent 'n' entries. otherwise memory would grow forever
        // and drawing would get slower and slower.
        public int maxLogCount = 50;

        // log as queue so we can remove the first entry easily
        Queue<LogEntry> log = new Queue<LogEntry>();


        Queue<LogEntry> filePath = new Queue<LogEntry>();
        int filecount = 0;

        int FirstimportFileDir=0;

        // hotkey to show/hide at runtime for easier debugging
        // (sometimes we need to temporarily hide/show it)
        // => F12 makes sense. nobody can find ^ in other games.
        public KeyCode hotKey = KeyCode.F12;

        // GUI
        bool visible;
        Vector2 scroll = Vector2.zero;

        private bool isHadImportAndMustStore = false; //是否有重要报告，且重要报告后，没有连续100次log都是不重要的
        private bool isImportLogStore = false; //是否有重要报告，且重要报告后，没有连续100次log都是不重要的
        private int continuousNormalLog = 0; //连续不中要的log数

        private bool isInitLogStore = false;
        private string randomString="AAAA";

        void Awake()
        {

            isHadImportAndMustStore = false;
            isImportLogStore = false;

            randomString = GetRandomString();

            if ( Application.isEditor )
                return;

            Application.logMessageReceived += OnLog;
        }
        Coroutine deleteC;
        private void OnEnable() {
            if ( deleteC !=null) {
                StopCoroutine(deleteC);
            }
            deleteC = StartCoroutine(DeleteFile());
        }

        private void OnDisable() {
            if ( deleteC != null ) {
                StopCoroutine(deleteC);
                deleteC = null;
            }
        }


        // OnLog logs everything, even Debug.Log messages in release builds
        // => this makes a lot of things easier. e.g. addon initialization logs.
        // => it's really better to have than not to have those
        void OnLog(string message, string stackTrace, LogType type)
        {
            if ( haserror == true )
                return;
            if ( log.Count > 2000 )
                return;

            // is this important?
            // => always show exceptions & errors
            // => usually a good idea to show warnings too, otherwise it's too
            //    easy to miss OnDeserialize warnings etc. in builds
            bool isImportant = type == LogType.Error || type == LogType.Exception || type == LogType.Warning;

            // use stack trace only if important
            // (otherwise users would have to find and search the log file.
            //  seeing it in the console directly is way easier to deal with.)
            // => only add \n if stack trace is available (only in debug builds)
            if ( isImportant && !string.IsNullOrWhiteSpace(stackTrace) ) {
                message = $"\n-{type}\n" + message;
                message += $"\n<<<< stackTrace >>>>\n{stackTrace}\n--";
            }

            //handle for store init log Start
            if ( isInitLogStore == false ) {
                if ( isImportant == false ) {
                    if ( log.Count > 1000 ) {
                        StoreLogToFile( logFileType._IInitLog);
                        isInitLogStore = true;
                    }
                } else {
                    StoreLogToFile(logFileType._IInitLog);
                    isInitLogStore = true;
                }
            }//handle for store init log End


            // add to queue
            log.Enqueue(new LogEntry(message, type));

            if ( isInitLogStore == false ) {
                return;
            }

            //isHadImportAndMustStore 标识重要的log，且要保存的标志
            if ( isImportant && isHadImportAndMustStore==false ) {
                isHadImportAndMustStore = true;
            }

            if ( isImportant ) {
                continuousNormalLog = 0;
            } else {
                continuousNormalLog++;
            }

            if ( continuousNormalLog >= 100 ) {
                if ( isHadImportAndMustStore && isImportLogStore==false ) {
                    StoreLogToFile(  logFileType._ImportLog); 
                }
                
                isHadImportAndMustStore = false;
                isImportLogStore = false;
            }


            if ( isHadImportAndMustStore ) {
                if ( log.Count == 100 ) {
                    StoreLogToFile(logFileType._ImportLog);
                    isImportLogStore = true;
                }
                if ( isImportLogStore ) {
                    // respect max entries
                    if ( log.Count > maxLogCount )
                        log.Dequeue();
                }

            } else {
                // respect max entries
                if ( log.Count > maxLogCount )
                    log.Dequeue();
            }



            // become visible if it was important
            // (no need to become visible for regular log. let the user decide.)
            if ( isImportant )
                visible = true;

            // auto scroll
            scroll.y = float.MaxValue;
        }

        void Update()
        {
            if (Input.GetKeyDown(hotKey))
                visible = !visible;

        }

        void OnGUI()
        {
            if (!visible) return;


            if ( NetworkClient.active ) {

                scroll = GUILayout.BeginScrollView(scroll, "Box", GUILayout.Width(Screen.width), GUILayout.Height(Screen.height));

                foreach ( LogEntry entry in log ) {
                    if ( entry.type == LogType.Error || entry.type == LogType.Exception )
                        GUI.color = Color.red;
                    else if ( entry.type == LogType.Warning )
                        GUI.color = Color.yellow;

                    GUILayout.Label(entry.message);
                    GUI.color = Color.white;
                }
                foreach ( LogEntry entry in filePath ) {
                    GUI.color = Color.yellow;
                    GUILayout.Label(entry.message);
                }
                GUILayout.EndScrollView();
            }
        }

        private void OnApplicationQuit() {
            if ( isHadImportAndMustStore && isImportLogStore == false ) {
                StoreLogToFile();
            }
        }

        void StoreLogToFile(logFileType typeLogFile = logFileType._ImportLog) {
            DebugMy.Log("========================> IMPORT LOG!! Start", this, true);
            DateTime time = System.DateTime.Now;

            if ( typeLogFile == logFileType._ImportLog ) {
                //path = Path.Combine(path, time.Year + "_" + time.Month + "_" + time.Day+"_"+ importFileDirNum);
                FirstimportFileDir ++;
            }

            string path = Path.Combine(Application.persistentDataPath, LogFileDirName);
            if ( typeLogFile == logFileType._IInitLog ) {
                path = Path.Combine(path, $"{time.Year:D4}" + "" + $"{time.Month:D2}"  + "" + $"{time.Day:D2}"  + "" + $"{time.Hour:D2}" + "" + $"{time.Minute:D2}" + randomString + typeLogFile.ToString() + deleteFlag +
                    $"{time.Year:D4}" + "_" + $"{time.Month:D2}" + "_" + $"{time.Day:D2}" + "_" + $"{time.Hour:D2}" + deleteFlag + "INIT" + deleteFlag);
            } else {
                path = Path.Combine(path, $"{time.Year:D4}" + "" + $"{time.Month:D2}" + "" + $"{time.Day:D2}" + "" + $"{time.Hour:D2}" + "" + $"{time.Minute:D2}" + randomString + typeLogFile.ToString() + deleteFlag +
                    $"{time.Year:D4}" + "_" + $"{time.Month:D2}" + "_" + $"{time.Day:D2}" + "_" + $"{time.Hour:D2}" + deleteFlag + FirstimportFileDir + deleteFlag);
            }

            if ( !Directory.Exists(path) ) {
                Directory.CreateDirectory(path);
            }

            path = Path.Combine(path, "M_" + $"{time.Minute:D2}" + "_" + $"{time.Second:D2}" + "_" + $"{time.Millisecond:D2}"+ "_" + typeLogFile.ToString() + ".txt");

            StreamWriter sw = new StreamWriter(path);
            sw.WriteLine("---------------begin\n");

            foreach ( var entry in log ) {
                sw.WriteLine(entry.message);
            }
            sw.WriteLine("\n---------------end");
            sw.Close();

            log.Clear();

            filecount++;
            filePath.Enqueue(new LogEntry(path+"     [Num:"+ filecount+"]", LogType.Error));
            if ( filePath.Count > 6 ) {
                filePath.Dequeue();
            }
            DebugMy.Log("========================> IMPORT LOG!! End " + path, this, true);
        }

        string LogFileDirName = "01_wangcq327";

        string deleteFlag = "_delete_";

        bool haserror = false;



        // 一个月前的全删
        //一天前的保留 INIT及第一个Improtlog
        //一个消失前的保留INIT及前20个ImprotLog

        IEnumerator DeleteFile() {

            while ( true ) {

                yield return new WaitForSeconds(1);

                DateTime time = System.DateTime.Now;
                int currentHour, currentDay, currentMonth;

                currentHour = int.Parse($"{time.Year:D4}" + "" + $"{time.Month:D2}" + "" + $"{time.Day:D2}" + "" + $"{time.Hour:D2}");
                currentDay = int.Parse($"{time.Year:D4}" + "" + $"{time.Month:D2}" + "" + $"{time.Day:D2}");
                currentMonth = int.Parse($"{time.Year:D4}" + "" + $"{time.Month:D2}");
                if ( System.IO.Directory.Exists(Path.Combine(Application.persistentDataPath, LogFileDirName)) ==false) {
                    continue;
                }
                string[] dirs = System.IO.Directory.GetDirectories(Path.Combine(Application.persistentDataPath, LogFileDirName));

                //202311031353_IInitLog__delete_2023_11_03_13_delete_INIT_delete_
                foreach ( string dir in dirs ) {
                    try {

                        if ( dir.Contains(deleteFlag) == false )
                            continue;

                        //2023_11_03_13 INIT
                        string[] xxxx = dir.Split(deleteFlag);

                        //2023 11 03 13
                        string[] stt = xxxx[1].Split("_");
                        //Debug.Log(xxxx[1]+"|"+ xxxx[2]);
                        int oldMouth = int.Parse(stt[0] + stt[1]);
                        int oldDay = int.Parse(stt[0] + stt[1] + stt[2]);
                        int oldHour = int.Parse(stt[0] + stt[1] + stt[2] + stt[3]);

                        if ( IsOneMonthAgon(oldMouth, currentMonth) ) {
                            Directory.Delete(dir, true);
                        }

                        if ( IsOneDayAgon(oldDay, currentDay) && (xxxx.Length > 2 && xxxx[2] != "INIT" && xxxx[2] != "1") ) {
                            Directory.Delete(dir, true);
                        }

                        if ( IsOneHourAgon(oldHour, currentHour) && (xxxx.Length > 2 && xxxx[2] != "INIT") ) {

                            if ( int.Parse(xxxx[2]) > 20 ) {
                                Directory.Delete(dir, true);
                            }
                        }

                        haserror = false;
                    } catch ( Exception e ) {
                        Debug.Log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:" + dir);
                        Directory.Delete(dir, true);
                        Debug.Log(e);
                        haserror = true;
                        yield break;
                    }
                }

            }

            deleteC = null;
        }



        bool IsOneMonthAgon(int old, int curr) {
            if ( curr > old )
                return true;
            return false;
        }

        bool IsOneDayAgon(int old, int curr) {
            if ( curr > old )
                return true;
            return false;
        }

        bool IsOneHourAgon(int old, int curr) {
            if ( curr > old )
                return true;
            return false;
        }

        string GetRandomString() {
            int length = 5; // 生成的随机字符串的长度
            string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; // 可以包含在随机字符串中的字符

            StringBuilder randomString = new StringBuilder();
            System.Random random = new System.Random();

            for ( int i = 0; i < length; i++ ) {
                int index = random.Next(0, characters.Length); // 生成一个随机字符的索引
                randomString.Append(characters[index]); // 将随机字符添加到随机字符串中
            }

            return  randomString.ToString();
        }
    }
}


namespace Mirror
{
    internal class DebugMy
    {

        static string Tag;
        public static bool isShowNormalLog = false;
        public static bool isShowErrorLog = true;

        private static string sdkVersion = "";
        private static DateTime currentNow;
        internal static void Log(string msg, object o, bool current = false, string userTag = "", bool all = false) {
            if ( all == true ) {
                isShowNormalLog = true;
            }

            if ( o is true ) {

            } else {
                if ( isShowNormalLog == false && current == false ) {
                    return;
                }
            }

            currentNow = System.DateTime.Now;
            Tag = "[" + currentNow + $".{currentNow.Millisecond}]" + "[ WCQ" + sdkVersion + " ][ FC:" + $"{Time.frameCount:D6}" + " ]  ";

            if ( o == null ) {
                if ( Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.WindowsEditor ) {
                    Debug.Log(Tag + msg);
                } else {
                    Console.WriteLine(Tag + msg);
                }
            } else {
                if ( Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.WindowsEditor ) {
                    Debug.Log(Tag + "[" + o.GetType().ToString() + $"({o.GetHashCode()})" + "]: " + msg);
                } else {
                    Console.WriteLine(Tag + "[" + o.GetType().ToString() + $"({o.GetHashCode()})" + "]: " + msg);
                }
            }

        }

        internal static void LogError(string msg, object o, string userTag = "") {
            if ( isShowErrorLog == false )
                return;

            currentNow = System.DateTime.Now;
            Tag = "[" + currentNow + $".{currentNow.Millisecond}]" + "[ WCQ" + sdkVersion + " ][ FC:" + $"{Time.frameCount:D6}" + " ]  ";

            if ( o == null ) {
                if ( Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.WindowsEditor ) {
                    Debug.LogError(Tag + msg);
                } else {
                    Console.WriteLine(Tag + msg);
                }
            } else {
                if ( Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.WindowsEditor ) {
                    Debug.LogError(Tag + "[" + o.GetType().ToString() + $"({o.GetHashCode()})" + "]: " + msg);
                } else {
                    Console.WriteLine(Tag + "[" + o.GetType().ToString() + $"({o.GetHashCode()})" + "]: " + msg);
                }
            }
        }
    }
}